﻿using RockBus.Utilities;
using System;
using System.Diagnostics;
using System.IO;
using System.ServiceModel.Channels;
using System.Text;
using System.Threading;
using System.Xml;

namespace RockBus.ServiceModel
{
    public class MessageWrapper : IDisposable
    {
        public const int DefaultMaxBufferSize = 4 * 1024 * 1024;

        public MessageWrapper(Message message, bool useBuffer, int maxBufferSize = DefaultMaxBufferSize)
        {
            this._message = message;
            this.UseBuffer = useBuffer;
            this.MaxBufferSize = maxBufferSize;

            if (this.UseBuffer)
            {
                this._messageBuffer = message.CreateBufferedCopy(maxBufferSize);
            }

            this.Action = message.Headers.Action;
            this.Headers = message.Headers;
            this.Properties = message.Properties;
            this.Version = message.Version;
            this.IsEmpty = message.IsEmpty;
            this.IsFault = message.IsFault;
        }

        private readonly MessageBuffer _messageBuffer;

        public MessageBuffer MessageBuffer
        {
            get { return this._messageBuffer; }
        }

        private readonly Message _message;

        public Message Message
        {
            get
            {
                if (this.UseBuffer)
                {
                    return _messageBuffer.CreateMessage();
                }

                this.CheckMessageValidity();
                return this._message;
            }
        }

        public bool UseBuffer { get; set; }

        public int MaxBufferSize { get; set; }

        #region Message Properties (not delegated because of ObjectDisposedExceptions)

        public string Action { get; private set; }

        public MessageHeaders Headers { get; private set; }

        public MessageProperties Properties { get; private set; }

        public MessageVersion Version { get; private set; }

        public string GetMessageVersionAsString()
        {
            return Conversions.ConvertWithTypeConverter<MessageVersion, string>(this.Version);
        }

        public bool IsEmpty { get; private set; }

        public bool IsFault { get; private set; }

        #endregion Message Properties (not delegated because of ObjectDisposedExceptions)

        public Message MarshalMessage(MessageVersion messageVersion)
        {
            return this.Message.MarshalMessage(this.Headers.To, messageVersion);
        }

        public byte[] ConvertToByteArray()
        {
            using (var ms = new MemoryStream())
            using (var writer = XmlWriter.Create(ms))
            {
                this.Message.WriteMessage(writer);
                writer.Flush();
                ms.Flush();
                return ms.ToArray();
            }
        }

        public string MessageAsString()
        {
            return this.Message.MessageAsString();
        }

        //public byte[] ConvertToByteArray()
        //{
        //    var settings = new XmlWriterSettings();
        //    settings.Encoding = System.Text.Encoding.UTF8;

        //    using (var stream = new MemoryStream())
        //    using (var writer = XmlWriter.Create(stream, settings))
        //    {
        //        this.Message.WriteMessage(writer);
        //        writer.Flush();
        //        stream.Flush();

        //        byte[] retval = stream.ToArray();
        //        return retval;
        //    }
        //}

        public XmlReader GetReaderAtBodyContents()
        {
            return this.Message.GetReaderAtBodyContents();
        }

        private bool _consumed;

        [Conditional("DEBUG")]
        private void CheckMessageValidity()
        {
            Debug.Assert(!_consumed, "The message may have been already consumed once before.");
            _consumed = true;
        }

        private int _disposed;

        public void Dispose()
        {
            // Make sure we Dispose only once
            if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 1)
            {
                return;
            }

            if (null != this._messageBuffer)
            {
                ((IDisposable)this._messageBuffer).Dispose();
            }
            if ((null != this._message) &&
                (this._message.State != MessageState.Closed))
            {
                ((IDisposable)this._message).Dispose();
            }
        }
    }
}